package com.matrix.order.infrastructure.persistence.repository.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.matrix.common.enums.OrderStatusEnum;
import com.matrix.order.converter.OrderConverter;
import com.matrix.order.domain.model.Order;
import com.matrix.order.domain.model.OrderItem;
import com.matrix.order.domain.model.OrderPayment;
import com.matrix.order.domain.repository.OrderRepository;
import com.matrix.order.infrastructure.persistence.mapper.OrderItemMapper;
import com.matrix.order.infrastructure.persistence.mapper.OrderMapper;
import com.matrix.order.infrastructure.persistence.mapper.OrderPaymentMapper;
import com.matrix.order.infrastructure.persistence.po.OrderItemPO;
import com.matrix.order.infrastructure.persistence.po.OrderPO;
import com.matrix.order.infrastructure.persistence.po.OrderPaymentPO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author 有点甜
 * @since 2025/8/2
 */
@Repository
@RequiredArgsConstructor
public class OrderRepositoryImpl implements OrderRepository {

    private final OrderMapper orderMapper;
    private final OrderItemMapper orderItemMapper;
    private final OrderPaymentMapper orderPaymentMapper;

    @Override
    public Optional<Order> findById(Long orderId) {
        OrderPO orderPO = orderMapper.selectById(orderId);
        if (orderPO == null) {
            return Optional.empty();
        }
        return Optional.of(buildOrderAggregate(orderPO));
    }

    @Override
    public Optional<Order> findByOrderNo(String orderNo) {
        OrderPO orderPO = orderMapper.selectOne(new LambdaQueryWrapper<OrderPO>().eq(OrderPO::getOrderNo, orderNo));
        if (orderPO == null) {
            return Optional.empty();
        }
        return Optional.of(buildOrderAggregate(orderPO));
    }

    @Override
    public Order findByPaymentNo(String paymentNo) {
        Long orderId = orderPaymentMapper.findOrderIdByPaymentNo(paymentNo);
        if (orderId == null) {
            return null;
        }
        return findById(orderId).orElse(null);
    }

    @Override
    public Optional<Order> findByOrderItemId(Long orderItemId) {
        Long orderId = orderItemMapper.findOrderIdByOrderItemId(orderItemId);
        if (orderId == null) {
            return Optional.empty();
        }
        return findById(orderId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(Order order) {
        OrderPO orderPO = OrderConverter.INSTANCE.toOrderPO(order);

        if (order.getId() == null) {
            orderMapper.insert(orderPO);
            order.setId(orderPO.getId());
        } else {
            orderMapper.updateById(orderPO);
        }

        syncOrderItems(order);
        syncOrderPayments(order);
    }

    @Override
    public Page<Order> findPageByShopId(Long shopId, Integer status, int pageNum, int pageSize) {
        Page<OrderPO> poPage = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<OrderPO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OrderPO::getShopId, shopId)
                .eq(status != null, OrderPO::getStatus, status)
                .orderByDesc(OrderPO::getCreateTime);
        orderMapper.selectPage(poPage, queryWrapper);

        Page<Order> domainPage = new Page<>(poPage.getCurrent(), poPage.getSize(), poPage.getTotal());
        List<Order> orders = poPage.getRecords().stream()
                .map(this::buildOrderAggregate)
                .collect(Collectors.toList());
        domainPage.setRecords(orders);
        return domainPage;
    }

    @Override
    public Page<Order> findPageByUserId(Long userId, List<OrderStatusEnum> statuses, int pageNum, int pageSize) {
        Page<OrderPO> poPage = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<OrderPO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OrderPO::getUserId, userId);
        if (!CollectionUtils.isEmpty(statuses)) {
            queryWrapper.in(OrderPO::getStatus, statuses);
        }
        queryWrapper.orderByDesc(OrderPO::getCreateTime);
        orderMapper.selectPage(poPage, queryWrapper);

        Page<Order> domainPage = new Page<>(poPage.getCurrent(), poPage.getSize(), poPage.getTotal());
        List<Order> orders = poPage.getRecords().stream()
                .map(this::buildOrderAggregate)
                .collect(Collectors.toList());
        domainPage.setRecords(orders);
        return domainPage;
    }

    @Override
    public Map<Long, Integer> getMonthlySalesByShopIds(List<Long> shopIds) {
        LocalDateTime thirtyDaysAgo = LocalDateTime.now().minusDays(30);
        Map<Long, Map<String, Object>> results = orderItemMapper.sumSalesByShopIdsAfterDate(shopIds, thirtyDaysAgo);

        if (results == null || results.isEmpty()) {
            return Collections.emptyMap();
        }

        return results.entrySet().stream().collect(Collectors.toMap(
                Map.Entry::getKey,
                entry -> ((Number) entry.getValue().get("total_sales")).intValue()
        ));
    }

    @Override
    public Map<Long, Integer> getProductMonthlySales() {
        LocalDateTime thirtyDaysAgo = LocalDateTime.now().minusDays(30);
        List<Map<String, Object>> results = orderItemMapper.sumProductSalesAfterDate(thirtyDaysAgo);

        if (results == null || results.isEmpty()) {
            return Collections.emptyMap();
        }

        return results.stream().collect(Collectors.toMap(
                map -> ((Number) map.get("product_id")).longValue(),
                map -> ((Number) map.get("total_sales")).intValue()
        ));
    }

    private void syncOrderItems(Order order) {
        orderItemMapper.delete(new LambdaQueryWrapper<OrderItemPO>().eq(OrderItemPO::getOrderId, order.getId()));
        if (!CollectionUtils.isEmpty(order.getItems())) {
            order.getItems().forEach(item -> {
                item.setOrderId(order.getId());
                OrderItemPO itemPO = OrderConverter.INSTANCE.toOrderItemPO(item);
                orderItemMapper.insert(itemPO);
                item.setId(itemPO.getId());
            });
        }
    }

    private void syncOrderPayments(Order order) {
        if (!CollectionUtils.isEmpty(order.getPayments())) {
            order.getPayments().stream()
                    .filter(p -> p.getId() == null)
                    .forEach(payment -> {
                        payment.setOrderId(order.getId());
                        OrderPaymentPO paymentPO = OrderConverter.INSTANCE.toOrderPaymentPO(payment);
                        orderPaymentMapper.insert(paymentPO);
                        payment.setId(paymentPO.getId());
                    });
        }
    }

    private Order buildOrderAggregate(OrderPO orderPO) {
        Order order = OrderConverter.INSTANCE.toOrder(orderPO);
        order.setItems(findItemsByOrderId(order.getId()));
        order.setPayments(findPaymentsByOrderId(order.getId()));
        return order;
    }

    private List<OrderItem> findItemsByOrderId(Long orderId) {
        List<OrderItemPO> poList = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItemPO>().eq(OrderItemPO::getOrderId, orderId));
        return poList.stream().map(OrderConverter.INSTANCE::toOrderItem).collect(Collectors.toList());
    }

    private List<OrderPayment> findPaymentsByOrderId(Long orderId) {
        List<OrderPaymentPO> poList = orderPaymentMapper.selectList(new LambdaQueryWrapper<OrderPaymentPO>().eq(OrderPaymentPO::getOrderId, orderId));
        return poList.stream().map(OrderConverter.INSTANCE::toOrderPayment).collect(Collectors.toList());
    }
}
